/*

  json_array.h
  Shan.Lib

 Copyright (c) 2013, Sung-Han Park
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 1. Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef _Shan_JSON_json_array_h_
#define _Shan_JSON_json_array_h_

#include <vector>
#include <memory>
#include <sstream>
#include "json_exception.h"
#include "json_value.h"
#include "json_string.h"
#include "json_number.h"
#include "json_true.h"
#include "json_false.h"
#include "json_null.h"

namespace shan {
	namespace json {
		class object;

		class array : public json::value, public std::vector<json::value::value_ptr> {
		public:
			typedef std::vector<json::value::value_ptr> base_type;

			// constructor overloads
			array() {
			}

			array(const array& array) :
			base_type(array) {
			}

			array(array&& array) :
			base_type(std::move(array)) {
			}

			array(const base_type& array) :
			base_type(array) {
			}

			array(base_type&& array) :
			base_type(std::move(array)) {
			}

			// destructor
			virtual ~array() {}

			// assignment operator overloads
			array& operator=(const array& array) {
				base_type::operator=(array);
				return *this;
			}

			array& operator=(array&& array) {
				base_type::operator=(std::move(array));
				return *this;
			}

			array& operator=(const base_type& array) {
				base_type::operator=(array);
				return *this;
			}

			array& operator=(base_type&& array) {
				base_type::operator=(std::move(array));
				return *this;
			}

			// add functions
			void add(const json::object& val);
			void add(json::object&& val);
			void add(const json::array& val);
			void add(json::array&& val);
			void add(const json::string& val);
			void add(json::string&& val);
			void add(const json::number val);
			void add(const json::true_value val);
			void add(const json::false_value val);
			void add(const json::null_value val);
			void add(const std::string& val);
			void add(std::string&& val);
			void add(const char* val);
			void add(char val);
			void add(unsigned char val);
			void add(short val);
			void add(unsigned short val);
			void add(int val);
			void add(unsigned int val);
			void add(long val);
			void add(unsigned long val);
			void add(long long val);
			void add(unsigned long long val);
			void add(float val);
			void add(double val);
			void add(bool val);
			void add(std::nullptr_t val);

			// get functions
			std::shared_ptr<json::object> get_object(std::size_t inx, std::shared_ptr<json::object> defValue = std::shared_ptr<json::object>());
			std::shared_ptr<json::array> get_array(std::size_t inx, std::shared_ptr<json::array> defValue = std::shared_ptr<json::array>());

			std::string get_string(std::size_t inx, std::string defValue = std::string()) {
				try {
					return base_type::at(inx)->to_string();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			int get_int(std::size_t inx, int defValue = 0) {
				try {
					return base_type::at(inx)->to_int();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			long get_long(std::size_t inx, long defValue = 0) {
				try {
					return base_type::at(inx)->to_long();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			long long get_long_long(std::size_t inx, long long defValue = 0) {
				try {
					return base_type::at(inx)->to_long_long();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			float get_float(std::size_t inx, float defValue = 0) {
				try {
					return base_type::at(inx)->to_float();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			double get_double(std::size_t inx, double defValue = 0) {
				try {
					return base_type::at(inx)->to_double();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			bool get_bool(std::size_t inx, bool defValue = false) {
				try {
					return base_type::at(inx)->to_bool();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			virtual int to_int() {
				throw json::exception("json::array is not compatible with int type.");
			}

			virtual long to_long() {
				throw json::exception("json::array is not compatible with long type.");
			}

			virtual long long to_long_long() {
				throw json::exception("json::array is not compatible with long long type.");
			}

			virtual float to_float() {
				throw json::exception("json::array is not compatible with float type.");
			}

			virtual double to_double() {
				throw json::exception("json::array is not compatible with double type.");
			}

			virtual bool to_bool() {
				throw json::exception("json::array is not compatible with bool type.");
			}

			virtual bool is_null() {
				return false; // even empty array is not null
			}
			
			virtual std::string to_json() const;
			virtual std::string to_string() const {
				return to_json();
			}
			
		protected:
			virtual void internal_parse(std::istream& is);
		};
	}
}

#endif
